#pragma once

#include <iostream>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace socket_ns;

class EPollServer
{
    // 总的文件描述符个数
    const static int gnum = sizeof(fd_set) * 8;
    const static int gdefaultfd = -1;

public:
    EPollServer(uint16_t port)
        : _port(port), _listensocket(std::make_unique<TcpSocket>()) // 匿名对象
    {
        _listensocket->BuildListenSocket(_port);
        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create error!\n");
            exit(1);
        }
        LOG(INFO, "epoll create success, epfd: %d\n", _epfd);
    }

    void InitServer()
    {
        struct epoll_event ev;
        ev.events = EPOLLIN | EPOLLET;
        ev.data.fd = _listensocket->Sockfd();

        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensocket->Sockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll_ctl error!\n");
            exit(2);
        }
        LOG(INFO, "epoll_ctl success, add new sockfd : %d\n", _listensocket->Sockfd());
    }

    std::string EventsToString(uint32_t events)
    {
        std::string eventstr;
        if (events & EPOLLIN)
            eventstr = "EPOLLIN";
        if (events & EPOLLOUT)
            eventstr += "|EPOLLOUT";
        return eventstr;
    }

    void Loop()
    {
        int timeout = -1;
        while (true)
        {
            int n = ::epoll_wait(_epfd, revs, gnum, timeout);
            if (n > 0)
            {
                LOG(INFO, "haved event ready, n : %d\n", n); // 如果事件就绪，但是不处理，poll会一直通知我，直到我处理了！
                HandlerEvent();
                PrintDebug();
            }
            else if (n == 0)
                LOG(DEBUG, "time out\n");
            else
                LOG(ERROR, "select error\n");
        }
    }

    void HandlerEvent() // 分为IO事件或者是listen事件
    {
        for (int i = 0; i < gnum; ++i)
        {
            int fd = revs[i].data.fd;
            uint32_t revents = revs[i].events;
            LOG(INFO, "%d 上面有事件就绪了,具体事件是: %s\n", fd, EventsToString(revents).c_str());
            if (revents & EPOLLIN)
            {
                if (fd == _listensocket->Sockfd())
                    Accepter();
                else
                    HandlerIO(fd);
            }
        }
    }

    void Accepter()
    {
        InetAddr addr;
        int sockfd = _listensocket->Accepter(&addr);
        if (sockfd > 0)
        {
            LOG(DEBUG, "get a new link, client info %s:%d\n", addr.Ip().c_str(), addr.Port());
            struct epoll_event ev;
            ev.data.fd = sockfd;
            ev.events = EPOLLIN;
            ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
            LOG(INFO, "epoll_ctl success, add new sockfd : %d\n", sockfd);
        }
    }

    void HandlerIO(int fd)
    {
        char buffer[1024];
        ssize_t n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client say# " << buffer << std::endl;
            std::string content = "<html><body><h1>Hello lazy</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            // echo_str += buffer;
            ::send(fd, echo_str.c_str(), echo_str.size(), 0); // 临时方案
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit...\n");
            // 关闭fd
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
        else
        {
            LOG(INFO, "client quit...\n");
            // 关闭fd
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
    }

    void PrintDebug()
    {
        std::cout << "fd list: ";
        for (int i = 0; i < gnum; i++)
        {
            if (revs[i].data.fd == gdefaultfd)
                continue;
            std::cout << revs[i].data.fd << " ";
        }
        std::cout << "\n";
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensocket;

    int _epfd;
    struct epoll_event revs[gnum];
};